////////////////////////////////////////////////////////////////////////////
//
//  CryEngine Source File.
//  Copyright (C), Crytek.
// -------------------------------------------------------------------------
//  File name:   FileReadAheadCache.h
//  Created:     18/08/2010 by Timur.
//  Description: Read Ached cache for file operations.
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#ifndef __FILE_READ_AHEAD_CACHE_HDR__
#define __FILE_READ_AHEAD_CACHE_HDR__

#include "StreamAsyncFileRequest.h"

#define MAX_OPENED_READ_AHEAD_FILES 32
#define READ_AHEAD_CACHE_FILE_HANDLE_FIRST (256)
#define READ_AHEAD_CACHE_FILE_HANDLE_LAST  (READ_AHEAD_CACHE_FILE_HANDLE_FIRST+MAX_OPENED_READ_AHEAD_FILES)

class CFileReadAheadCache;
class CStreamEngine;

typedef std::vector<const char*> FileReadAheadRequestSequence;

//////////////////////////////////////////////////////////////////////////
// Thread that performs IO operations.
//////////////////////////////////////////////////////////////////////////
class CFileReadAheadWorkerThread : public CrySimpleThread<CFileReadAheadWorkerThread>, public CMultiThreadRefCount
{
public:
	CFileReadAheadWorkerThread( CFileReadAheadCache *pCache );
	~CFileReadAheadWorkerThread();

	void StartLoading( const FileReadAheadRequestSequence &sequence );

	//////////////////////////////////////////////////////////////////////////
	// CrySimpleThread
	//////////////////////////////////////////////////////////////////////////
	virtual void Run();
	virtual void Cancel();
	//////////////////////////////////////////////////////////////////////////

private:
	CFileReadAheadCache *m_pCache;
	CStreamEngine *m_pStreamEngine;

	FileReadAheadRequestSequence m_filesSequence;
	int m_nCurrentFile;

	volatile bool m_bCancelThreadRequest;
	CryEvent m_awakeEvent;
	string m_name;
};


//////////////////////////////////////////////////////////////////////////
// Read ahead cache try to preload files from the list before they are 
// actually requested by the engine.
//////////////////////////////////////////////////////////////////////////
class CFileReadAheadCache : public IAsyncIOFileCallback
{
public:
	typedef uint32 FileHandle;

	CFileReadAheadCache();
	~CFileReadAheadCache();

	void StartLoadingResourceList( const FileReadAheadRequestSequence &sequence );
	void StopLoadingResourceList();

	//////////////////////////////////////////////////////////////////////////
	// File interface.
	FILE*  FOpen( const char *filename );
	int    FSeek( FILE* file,int64 origin,int command );
	size_t FRead( FILE* file,void *data, size_t nElementSize, size_t nCount );
	int64  FTell( FILE* file );
	size_t FGetSize( FILE* file );
	int    FEof( FILE* file );
	void*  FGetCachedFileData( FILE *file,size_t &nFileSize );
	void   FClose( FILE* file );

	// File handle operations.
	bool       IsReadAheadFile( FILE *pFilePtr ) { return ToFileHandle(pFilePtr) != 0; };
	FileHandle ToFileHandle( FILE *pFilePtr );
	FILE*      FromFileHandle( FileHandle file );
	//////////////////////////////////////////////////////////////////////////

protected:
	struct SCacheEntry : public CMultiThreadRefCount
	{
		CryStringLocal m_sFilename;
		uint32 m_nCRC32;
		int m_nCounter;
		_smart_ptr<ICustomMemoryBlock> m_pMemoryBlock;
	};
	struct SOpenedFile
	{
		FileHandle m_file;
		int64 m_nCursor;
		int64 m_nFileSize;
		_smart_ptr<SCacheEntry> m_pCacheEntry;
		_smart_ptr<IMemoryBlock> m_pCachedMemory;
	};

	//////////////////////////////////////////////////////////////////////////
	// IAsyncIOFileCallback
	// Called after asynchronous file request was completed.
	//////////////////////////////////////////////////////////////////////////
	virtual void OnAsyncFinished( CAsyncIOFileRequest *pFileRequest );
	//////////////////////////////////////////////////////////////////////////

	void AddCacheEntry( CAsyncIOFileRequest *pFileRequest );
	void FreeCacheEntry( SCacheEntry *pCacheEntry );
	SCacheEntry* GetNewCacheEntry();

	void ClearCache();

	//////////////////////////////////////////////////////////////////////////
	SOpenedFile* GetOpenedFile( FILE* file );
	
private:
	typedef std::map<uint32,_smart_ptr<SCacheEntry> > CacheMap;
	CacheMap m_cacheMap;

	std::vector<SOpenedFile> m_openedFiles;

	_smart_ptr<CFileReadAheadWorkerThread> m_workerThread;
	bool m_bReadAheadEnabled;

	int m_nCounter;

	CryCriticalSection m_cacheLock;
	CryCriticalSection m_openedFilesLock;
	Crc32Gen m_crc32gen;
	_smart_ptr<ICustomMemoryHeap> m_pMemoryHeap;
};

//////////////////////////////////////////////////////////////////////////
inline CFileReadAheadCache::FileHandle CFileReadAheadCache::ToFileHandle( FILE *pFilePtr )
{
	INT_PTR id = (INT_PTR)pFilePtr;
	if (id >= READ_AHEAD_CACHE_FILE_HANDLE_FIRST && id < READ_AHEAD_CACHE_FILE_HANDLE_LAST)
	{
		return (FileHandle)(id - READ_AHEAD_CACHE_FILE_HANDLE_FIRST);
	}
	return 0;
}

//////////////////////////////////////////////////////////////////////////
inline FILE* CFileReadAheadCache::FromFileHandle( FileHandle file )
{
	if (file)
	{
		FILE *pFile = (FILE*)(READ_AHEAD_CACHE_FILE_HANDLE_FIRST + (INT_PTR)file);
		return pFile;
	}
	return 0;
}

#endif //__FILE_READ_AHEAD_CACHE_HDR__